<template>
	<div>
		<h-behavior-captcha-background
			:background-image="backgroundImage"
			:canvas-width="canvasWidth"
			:canvas-height="canvasHeight"
			:loading="backgroundLoading"
			:success="isSuccess"
			:show-message="isShowMessage"
			:message="message"
		>
			<img class="jigsaw-slider" :src="jigsawImage" :height="canvasHeight" :style="`transform:translateX(${styleWidth - sliderSize}px)`" />
		</h-behavior-captcha-background>
		<h-behavior-captcha-control description="拖动滑块完成拼图" :size="sliderSize">
			<div class="range-slider" ref="RangeSlider" :style="`width:${styleWidth}px`">
				<div :class="['range-btn', { isDown: isMouseDown }]" :style="`width:${sliderSize}px`" @mousedown="onRangeMouseDown($event)">
					<div></div>
					<div></div>
					<div></div>
				</div>
			</div>
		</h-behavior-captcha-control>
	</div>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, toRefs, onMounted, onBeforeMount, watch, PropType, computed } from 'vue';

import { JigsawCaptcha } from '/@/lib/declarations';
import { useBehaviorCaptcha } from '/@/hooks';

import HBehaviorCaptchaBackground from './HBehaviorCaptchaBackground.vue';
import HBehaviorCaptchaControl from './HBehaviorCaptchaControl.vue';

export default defineComponent({
	name: 'HJigsawCaptcha',

	components: {
		HBehaviorCaptchaBackground,
		HBehaviorCaptchaControl,
	},

	props: {
		schema: { type: Object as PropType<JigsawCaptcha>, default: () => ({}) },
		canvasWidth: { type: Number, default: 310 },
		canvasHeight: { type: Number, default: 155 },
		sliderSize: { type: Number, default: 30 },
		loading: { type: Boolean, default: false },
	},

	setup(props, { emit }) {
		const RangeSlider = ref<HTMLDivElement | null>(null);

		const state = reactive({
			// 后台生成的背景图Base64
			backgroundImageBase64: '',
			// 后台生成的拼图滑块Base64
			jigsawImageBase64: '',
			// 鼠标点下去时父级的width
			startWidth: 50,
			// 鼠标按下时的X
			startX: 0,
			// 鼠标当前的偏移X
			newX: 0,
			// 鼠标是否在按钮上按下
			isMouseDown: false,
			// 为了解决Mac上的click BUG
			isCloseDown: false,
		});

		const { getImage, timeoutClear, message, canOperate, isSuccess, isLoading, isShowMessage, verifyCaptcha, reset } = useBehaviorCaptcha();

		onMounted(() => {
			addEventListener();
		});

		onBeforeMount(() => {
			removeEventListener();
		});

		/**
		 * 添加移动/结束事件监听
		 */
		const addEventListener = () => {
			document.addEventListener('mousemove', onRangeMouseMove, false);
			document.addEventListener('mouseup', onRangeMouseUp, false);
		};

		/**
		 * 移除移动/结束事件监听
		 */
		const removeEventListener = () => {
			timeoutClear();
			// document.body.removeChild(this.$el);
			document.removeEventListener('mousemove', onRangeMouseMove, false);
			document.removeEventListener('mouseup', onRangeMouseUp, false);
		};

		watch(
			() => props.schema,
			(newValue) => {
				resetStatus();
				state.backgroundImageBase64 = newValue.originalImageBase64;
				state.jigsawImageBase64 = newValue.sliderImageBase64;
			}
		);

		const resetStatus = () => {
			reset();
			// 鼠标点下去时父级的width
			state.startWidth = props.sliderSize;
			// 鼠标按下时的X
			state.startX = 0;
			// 鼠标当前的偏移X
			state.newX = 0;
		};

		/**
		 * styleWidth是底部用户操作的滑块的父级，就是轨道在鼠标的作用下应该具有的宽度
		 */
		const styleWidth = computed(() => {
			const w = state.startWidth + state.newX - state.startX;
			return w < props.sliderSize ? props.sliderSize : w > props.canvasWidth ? props.canvasWidth : w;
		});

		const backgroundImage = computed(() => {
			return getImage(state.backgroundImageBase64);
		});

		const jigsawImage = computed(() => {
			return getImage(state.jigsawImageBase64);
		});

		const backgroundLoading = computed(() => {
			return props.loading || isLoading.value;
		});

		// 鼠标按下准备拖动
		const onRangeMouseDown = (e: MouseEvent) => {
			if (canOperate) {
				state.isMouseDown = true;
				const slider = RangeSlider.value as HTMLDivElement;
				state.startWidth = slider.clientWidth;
				state.newX = e.clientX;
				state.startX = e.clientX;
			}
		};

		// 鼠标移动
		const onRangeMouseMove = (e: MouseEvent) => {
			if (state.isMouseDown) {
				e.preventDefault();
				state.newX = e.clientX;
			}
		};

		// 鼠标抬起
		const onRangeMouseUp = () => {
			if (state.isMouseDown) {
				state.isMouseDown = false;
				verify();
			}
		};

		const verify = () => {
			verifyCaptcha(
				'JIGSAW',
				{ x: state.newX - state.startX, y: 5 },
				() => {
					emit('verify', true);
				},
				() => {
					emit('verify', false);
					resetStatus();
					emit('reset');
				}
			);
		};

		return {
			...toRefs(state),
			backgroundImage,
			jigsawImage,
			styleWidth,
			onRangeMouseDown,
			RangeSlider,
			message,
			isSuccess,
			isShowMessage,
			backgroundLoading,
		};
	},
});
</script>

<style lang="scss">
.jigsaw-slider {
	position: absolute;
	top: 0;
	left: 0;
	height: 100%;
	z-index: 2;
}

.range-slider {
	position: absolute;
	height: 100%;
	width: 50px;
	background-color: rgba(106, 160, 255, 0.8);
	border-radius: 3px;
	.range-btn {
		position: absolute;
		display: flex;
		align-items: center;
		justify-content: center;
		right: 0;
		width: 50px;
		height: 100%;
		background-color: #fff;
		border-radius: 3px;
		box-shadow: 0 0 4px #ccc;
		cursor: pointer;
		& > div {
			width: 0;
			height: 40%;

			transition: all 200ms;
			&:nth-child(2) {
				margin: 0 4px;
			}
			border: solid 1px #6aa0ff;
		}
		&:hover,
		&.isDown {
			& > div:first-child {
				border: solid 4px transparent;
				height: 0;
				border-right-color: #6aa0ff;
			}
			& > div:nth-child(2) {
				border-width: 3px;
				height: 0;
				border-radius: 3px;
				margin: 0 6px;
				border-right-color: #6aa0ff;
			}
			& > div:nth-child(3) {
				border: solid 4px transparent;
				height: 0;
				border-left-color: #6aa0ff;
			}
		}
	}
}
</style>
